﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UIObjects.TreeViewInterfaces;
#if SILVERLIGHT
using AbstraX.ClientInterfaces;
#else
using AbstraX.ServerInterfaces;
#endif
using System.Collections.ObjectModel;
using AbstraX.Contracts;
using MvvmTreeView;
using System.Windows.Media.Imaging;
using System.Windows.Media;
using System.ServiceModel.DomainServices.Client;
using System.Windows.Controls;
using System.ComponentModel;
using UIObjects.DragDropInterfaces;
using System.Diagnostics;
using Entities = AbstraX.BindingsTreeEntities;

namespace AbstraX.ViewModels.BindingsTree
{
    [TreeNode(typeof(BindingsTreeNodeBase), "")]
    public abstract class BindingsTreeNodeBase : ITreeNode
    {
        protected IBindingsTreeService bindingsTreeService;
        protected TreeNodeCollection nodes;
        protected ITreeView treeView;
        protected bool isExpanded;
        protected bool isChildrenLoaded;
        protected bool allowDrop;
        protected bool hasChildrenQueried;
        protected BitmapImage layoverImage;
        public event ImageAvailableHandler ImageAvailable;
        public event PropertyChangedEventHandler PropertyChanged;
        public event ImageAvailableHandler GlyphAvailable;
        public event ImageAvailableHandler LayoverImageAvailable;
        public object TreeViewItem { get; set; }

        public BindingsTreeNodeBase(IBindingsTreeService bindingsTreeService, ITreeView treeView)
        {
            this.treeView = treeView;
            this.bindingsTreeService = bindingsTreeService;

            nodes = new TreeNodeCollection();
        }

        [TreeNodeProperty(TreeNodePropertyType.Text)]
        public abstract string Text { get; set; }
        public abstract void LoadChildren();
        protected abstract void ChildrenLoaded(IBindingsTreeCollection children);
        public abstract List<string> AddNewShortCuts { get; }
        public object ItemContent { get; set; }
        public abstract void AddNew(string shortCut);
        public abstract void DragStarting(object sender, UIObjects.DragDropInterfaces.ItemDragEventArgs e);
        public abstract void DragCompleted(object sender, UIObjects.DragDropInterfaces.ItemDragEventArgs e);
        public abstract void ItemDropped(object sender, UIObjects.DragDropInterfaces.ItemDragEventArgs e, int insertionIndex);
        public abstract void GiveFeedback(object sender, GiveFeedbackEventArgs e);
        public abstract void DragEnter(object sender, UIObjects.DragDropInterfaces.DragEventArgs e);
        public abstract void DragLeave(object sender, UIObjects.DragDropInterfaces.DragEventArgs e);
        public abstract void DragOver(object sender, UIObjects.DragDropInterfaces.DragEventArgs e);
        public abstract float ChildOrdinal { get; set; }
        public abstract bool? HasChildren { get; }
        public abstract Entity Entity { get; }
        public abstract void OnSelect();

        public bool IsChildrenLoaded
        {
            get
            {
                return isChildrenLoaded;
            }
        }

        public object BackColor
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        public object Bounds
        {
            get { throw new NotImplementedException(); }
        }

        public bool Checked
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        public ITreeNode FirstNode
        {
            get { throw new NotImplementedException(); }
        }

        public string FullPath
        {
            get { throw new NotImplementedException(); }
        }

        public bool IsEditing
        {
            get { throw new NotImplementedException(); }
        }

        public bool IsExpanded
        {
            get 
            {
                return isExpanded;
            }
        }

        public bool IsSelected
        {
            get { throw new NotImplementedException(); }
        }

        public bool IsVisible
        {
            get { throw new NotImplementedException(); }
        }

        public ITreeNode LastNode
        {
            get { throw new NotImplementedException(); }
        }

        public int Level
        {
            get { throw new NotImplementedException(); }
        }

        public ITreeNode NextNode
        {
            get { throw new NotImplementedException(); }
        }

        public ITreeNode NextVisibleNode
        {
            get { throw new NotImplementedException(); }
        }

        public void RemoveSpinster()
        {
            if (this.ContainsSpinster)
            {
                var treeViewItem = (TreeViewItem)this.TreeViewItem;
                var spinsterNode = (SpinsterNode) treeViewItem.Items.Cast<ITreeNode>().Where(n => n is SpinsterNode).Select(s => s).First();

                isExpanded = true;
                nodes.Remove(spinsterNode);

                PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs("Children"));
            }
        }

        public bool ContainsSpinster
        {
            get
            {
                if (this.TreeViewItem != null)
                {
                    return ((TreeViewItem)this.TreeViewItem).Items.Cast<ITreeNode>().Any(n => n is SpinsterNode);
                }
                else
                {
                    return false;
                }
            }
        }

        [TreeNodeChildSource(typeof(BindingsTreeNodeBase), DragDropEffects.Copy, "", "", "")]
        public virtual TreeNodeCollection Nodes
        {
            get 
            {
                if ((this.HasChildren ?? true) && (!this.ContainsSpinster) && (!this.IsChildrenLoaded))
                {
                    nodes.Add(new SpinsterNode(treeView));
                }

                return nodes;
            }
        }

        public ITreeNode Parent
        {
            get { throw new NotImplementedException(); }
        }

        public ITreeNode PrevNode
        {
            get { throw new NotImplementedException(); }
        }

        public ITreeNode PrevVisibleNode
        {
            get { throw new NotImplementedException(); }
        }

        public int SelectedImageIndex { get; set; }
        public string SelectedImageKey { get; set; }
        public int StateImageIndex { get; set; }
        public string StateImageKey { get; set; }
        public object Tag { get; set; }
        public int ImageIndex { get; set; }
        public string ImageKey { get; set; }

        public string ToolTipText
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        public ITreeView TreeView
        {
            get 
            {
                return treeView;
            }
        }

        public void BeginEdit()
        {
            throw new NotImplementedException();
        }

        public object Clone()
        {
            throw new NotImplementedException();
        }

        public void Collapse()
        {
            throw new NotImplementedException();
        }

        public void Collapse(bool ignoreChildren)
        {
            throw new NotImplementedException();
        }

        public void EndEdit(bool cancel)
        {
            throw new NotImplementedException();
        }

        public void EnsureVisible()
        {
            throw new NotImplementedException();
        }

        public void AddChildren(IBindingsTreeCollection children)
        {
#if SILVERLIGHT
            //BindingsTree
            //  BindingsTreeNode
            //    Folder
            //      Element
            //      DataContext
            //          Property
            //      PropertyBinding
            //        AttributeProperty
            //          AbstraXBindingSource
            //          QueryBindingSource

            var nodeIds = this.Nodes.Where(n => (!(n is SpinsterNode))).Select(e => (Entity)e.Tag).Select(e => e.GetID());

            children.AsQueryable().Cast<Entity>()
            .Select<Entity, BindingsTreeNodeBase>(e => GetTreeNode(e))
            .Where(b => !nodeIds.Any(id => id == ((Entity)(b.Tag)).GetID()))
            .ToList()
            .ForEach(b => this.Nodes.InsertOrderBy(t => t.ChildOrdinal).ThenBy(t => t.Text, b));  // always remember to include the second parameter to finalize
#endif
        }

        private BindingsTreeNodeBase GetTreeNode(Entity e)
        {
#if SILVERLIGHT
            switch (e.GetType().Name)
            {
                case "BindingsTreeEntity":
                    return new BindingsTreeNodeTree(bindingsTreeService, treeView, (Entities.BindingsTreeEntity)e);
                case "BindingsTreeNode":
                    return new BindingsTreeNodeTreeNode(bindingsTreeService, treeView, (Entities.BindingsTreeNode)e);
                case "BindingsTreeNodeReference":
                    return new BindingsTreeNodeTreeNodeReference(bindingsTreeService, treeView, (Entities.BindingsTreeNodeReference)e);
                case "Folder":
                    return new BindingsTreeNodeFolder(bindingsTreeService, treeView, (Entities.Folder)e);
                case "Element":
                    return new BindingsTreeNodeElement(bindingsTreeService, treeView, (Entities.Element)e);
                case "DataContextObject":
                    return new BindingsTreeNodeDataContext(bindingsTreeService, treeView, (Entities.DataContextObject)e);
                case "NodeProperty":
                    return new BindingsTreeNodeNodeProperty(bindingsTreeService, treeView, (Entities.NodeProperty)e);
                case "PropertyBinding":
                    return new BindingsTreeNodePropertyBinding(bindingsTreeService, treeView, (Entities.PropertyBinding)e);
                case "AttributeProperty":
                    return new BindingsTreeNodeAttribute(bindingsTreeService, treeView, (Entities.AttributeProperty)e);
                case "AbstraXBindingSource":
                    return new BindingsTreeNodeAbstraXBindingSource(bindingsTreeService, treeView, (Entities.AbstraXBindingSource)e);
                case "QueryBindingSource":
                    return new BindingsTreeNodeQueryBindingSource(bindingsTreeService, treeView, (Entities.QueryBindingSource)e);
                default:
                    Debugger.Break();
                    return null;
            }
#else
            return null;
#endif
        }

        public virtual void Expand()
        {
            if (!this.IsChildrenLoaded)
            {
                this.LoadChildren();
            }

            if (this.TreeViewItem != null)
            {
                ((TreeViewItem)this.TreeViewItem).IsExpanded = true;
                this.isExpanded = true;
            }
        }

        public virtual void ExpandAll()
        {
            this.Expand();
        }

        public int GetNodeCount(bool includeSubTrees)
        {
            throw new NotImplementedException();
        }

        public void Remove()
        {
            treeView.Nodes.Remove(this);
        }

        public void Toggle()
        {
            throw new NotImplementedException();
        }

        public virtual object Image
        {
            get
            {
                object tagObject = this.Tag;
                BitmapImage image = null;

#if SILVERLIGHT
                if (tagObject != null)
                {
                    var key = tagObject.GetType().Name;

                    if (treeView.ImageList.Images.ContainsKey(key))
                    {
                        image = treeView.ImageList.Images[key];
                    }
                }
#endif

                return image;
            }
        }

        public virtual void StartLoadImage()
        {
            var tagObject = this.Tag;

            if (tagObject != null)
            {
                var baseObject = this.Entity;
                var key = tagObject.GetType().Name;
                var imageList = treeView.ImageList;

                if (imageList.Images.ContainsKey(key))
                {
                    if (imageList.ImageLoadHandlers.ContainsKey(key))
                    {
                        var eventHandlers = imageList.ImageLoadHandlers[key];

                        foreach (ImageAvailableHandler handler in ImageAvailable.GetInvocationList())
                        {
                            eventHandlers.Add(handler);
                        }
                    }

                    if (ImageAvailable != null)
                    {
                        ImageAvailable(this);
                    }
                }
                else
                {
                    if (imageList.ImageLoadHandlers.ContainsKey(key))
                    {
                        var eventHandlers = imageList.ImageLoadHandlers[key];

                        foreach (ImageAvailableHandler handler in ImageAvailable.GetInvocationList())
                        {
                            eventHandlers.Add(handler);
                        }
                    }
                    else
                    {
#if SILVERLIGHT
                        var image = bindingsTreeService.LoadImageForItemType(key);

                        imageList.Images.Add(key, image, Colors.Transparent);
#endif
                        if (ImageAvailable != null)
                        {
                            ImageAvailable(this);
                        }
                    }
                }
            }

            if (this.HasLayoverImage(out layoverImage))
            {
                LayoverImageAvailable(this);
            }
        }

        public virtual ContextMenu ContextMenu
        {
            get
            {
                return null;
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        public ITreeNode ShallowClone()
        {
            throw new NotImplementedException();
        }

        public ITreeNode DeepClone()
        {
            throw new NotImplementedException();
        }

        public bool AllowRename
        {
            get
            {
                return false;
            }
        }

        public bool AllowDrop
        {
            get 
            {
                return false;
            }
        }

        public virtual bool HasLayoverImage<T>(out T image)
        {
            image = default(T);
            return false;
        }

        public object Glyph
        {
            get { throw new NotImplementedException(); }
        }

        public virtual object LayoverImage
        {
            get 
            {
                return layoverImage;
            }
        }

        public virtual string SelectionStatus
        {
            get
            {
                return null;
            }
        }
    }
}
